/*
 * test_family.cpp
 *
 *  Created on: Apr 7, 2014
 *      Author: killerg
 */

#include <map>
#include <list>
#include <queue>
#include <string.h>
#include <assert.h>
#include <functional>
#include "share/util.h"
#include "share/patterns.h"
#include "share/clock.h"
using namespace std;

#define record_of(memberaddr, type, member) \
	((type*)((uint8_t*)memberaddr - offsetof(type, member)))

#define SHMNAME "/hello1"

struct member_t {
	int64_t playerid;
	int32_t contrib;
	int8_t pos;
};

struct clan_t {
	int64_t id = -1;
	char name[20];
	char plan[40];
	char notice[40];
	member_t members[20];
	int64_t applies[20];
};

static const int CLAN_NUM = 5000;
static const int32_t BLOCKHEAD = 0xBEEFDEAD;
static const int32_t BLOCKTAIL = 0xDEADBEEF;
static const int32_t STUBHEAD = 0xCAFEBADD;
static const int32_t STUBTAIL = 0xBADDCAFE;

struct clanmgr_t {
	enum EClanOp {
		OpNone = 0,
		OpSync,
		OpDel,
	};
	struct stub_t {
		int32_t magic_head = STUBHEAD;
		EClanOp op = OpNone;
		size_t index = -1;
		clan_t data;
		int32_t magic_tail = STUBTAIL;
	};

	struct clanblock_t {
		int32_t last_validid;
		clan_t data[CLAN_NUM];
	};
	int64_t last_validid = 0;
	clanblock_t* shm;
	ptr_map<int64_t, stub_t*> stubs;
	priority_queue<size_t, vector<size_t>, greater<size_t>> free_indexes;
	set<stub_t*> needdeal;

	bool init() {
		shm = (clanblock_t*)shm_get(SHMNAME, sizeof(clanblock_t));

		if (shm) {
			last_validid = shm->last_validid;
			for (int i=0; i<CLAN_NUM; ++i) {
				clan_t* clan = shm->data + i;
				if (clan->id >= 0) {
					clan_t* added = clan_new(clan->id, false);
					stub_t* stub = record_of(added, stub_t, data);
					stub->index = i;
					stub->op = OpNone;
					*added = *clan;
				} else {
					free_indexes.push(i);
				}
			}
		} else {
			shm = (clanblock_t*)shm_new(SHMNAME, sizeof(clanblock_t));
			memset(shm, -1, sizeof(clanblock_t));
			for (int i=0; i<CLAN_NUM; ++i)
				free_indexes.push(i);

			shm->last_validid = 0;
		}

		return true;
	}
	void destroy() {
		for (auto &itr : stubs) {
			delete itr.second;
		}
		stubs.clear();
		needdeal.clear();
		shm_free(SHMNAME, sizeof(clanblock_t), shm);
	}

	void update() {
		shm->last_validid = last_validid;
		for (auto &itr : needdeal) {
			assert(itr->op != OpNone);
			assert(itr->magic_head == STUBHEAD && itr->magic_tail == STUBTAIL);
			switch (itr->op) {
			case OpDel:
				if (itr->index != size_t(-1)) {
					memset(shm->data + itr->index, -1, sizeof(clan_t));
					free_indexes.push(itr->index);
				}
				delete itr;
				break;
			case OpSync:
				if (itr->index == size_t(-1)) {
					itr->index = free_indexes.top();
					free_indexes.pop();
				}
				shm->data[itr->index] = itr->data;
				itr->op = OpNone;
				break;
			}
		}
		needdeal.clear();
	}


	clan_t* clan_new(int64_t id = -1, bool needsync = true) {
		if (id < 0)
			id = last_validid++;

		auto stub = new stub_t;
		stub->data.id = id;
		stubs.set(id, stub);
		if (needsync)
			post_sync(stub, OpSync);
		return &stub->data;
	}
	void clan_free(clan_t* clan) {
		auto stub = stubs.get(clan->id);
		stubs.erase(clan->id);
		post_sync(stub, OpDel);
	}
	void post_sync(stub_t* stub, EClanOp op = OpSync) {
		assert(stub->magic_head == STUBHEAD && stub->magic_tail == STUBTAIL);
		stub->op = op;
		needdeal.insert(stub);
	}
};

static clanmgr_t s_mgr;
static set<int64_t> s_exists;
static my_clock_t s_clock;

static void print_data(int tick = -1) {
	printf("------------tick:%d\n", tick);
#define log(name) printf(#name":%d\n", name.size())
	log(s_mgr.stubs);
	log(s_mgr.free_indexes);
	log(s_mgr.needdeal);
	log(s_exists);
#undef log
	printf("s_mgr.last_validid:%d\n", s_mgr.last_validid);
}

bool test_family() {
	assert(s_mgr.init());
	assert(s_clock.init(1));

	for (auto &itr : s_mgr.stubs) {
		s_exists.insert(itr.second->data.id);
	}

	print_data();
	sleep(1);
	for (int i=0; i<1; ++i) {
		s_clock.begin_update();
		for (int ii=0; ii<100; ++ii)
			if (rand() % 2) {
				clan_t* clan = s_mgr.clan_new();
				strncpy(clan->name, fmt_cstr("name:%d", clan->id), 20);
				strncpy(clan->plan, fmt_cstr("plan:%d", clan->id), 40);
				strncpy(clan->notice, fmt_cstr("notice:%d", clan->id), 40);
				s_exists.insert(clan->id);
			}

		list<clan_t*> erase;
		for (auto &exist : s_exists) {
			auto* stub = s_mgr.stubs.get(exist);
			switch (rand() % 3) {
			case 0:	// modify
			case 1:	// modify
				snprintf(stub->data.notice, 40, "%s", ctime(&s_clock.tt_now));
				s_mgr.post_sync(stub);
				break;
			case 2:	// delete
				//printf("%s", stub->data.notice);
				erase.push_back(&stub->data);
				break;
			}
		}
		for (auto &itr : erase) {
			s_exists.erase(itr->id);
			s_mgr.clan_free(itr);
		}

		printf("start commit\n");
		s_mgr.update();
		print_data(i);
		s_clock.end_update();
		s_clock.wait_frame();
		printf("sleep %6.6f", s_clock.frame_sleep);
	}

	s_clock.destroy();
	s_mgr.destroy();

	return true;
}


